package excel.server;

import excel.talkapi.fromclient.ClientRequest;
import excel.talkapi.fromclient.message.*;
import excel.talkapi.fromserver.ServerResponse;
import excel.talkapi.fromserver.message.ServerStatus;
import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.*;

class SocketCall implements Runnable {

    private final Socket socket;
    private ObjectInputStream ois;
    private ObjectOutputStream oos;

    private boolean cancel = false;
    private boolean used = false;

    private SXSSFWorkbook workbook;
    private SXSSFSheet sheet;
    private int rowIndex;

    private SimpleDateFormat simpleDateFormat;
    private Map<String, CellStyle> styleMap;
    private Map<String, CellStyle> bigDecimalStyleMap;


    public SocketCall(Socket socket) {
        this.socket = socket;
    }

    public synchronized boolean cancel() {
        if (used) {
            return false;
        } else {
            cancel = true;
            return true;
        }
    }

    public synchronized void run() {

        long tid = Thread.currentThread().getId();

        if (cancel) return;
        used = true;

        //开始对话
        try {
            ois = new ObjectInputStream(socket.getInputStream());
            oos = new ObjectOutputStream(socket.getOutputStream());
            ClientRequest clientRequest = (ClientRequest) ois.readObject();
            if (!Build.HELLO.equals(clientRequest.getActions()[0])) {
                throw new Exception("未知的请求来源");
            }
        } catch (ClassNotFoundException e) {
            closeAll();
            Logger.error("未知的请求来源", e);
            return;
        } catch (Exception e) {
            closeAll();
            Logger.error("对话建立失败", e);
            return;
        }

        Logger.info(tid + " ：开始处理");
        try {
            sendReadyMessage();

            workbook = new SXSSFWorkbook(200);
            CreationHelper createHelper = workbook.getCreationHelper();

            simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            styleMap = new HashMap<String, CellStyle>();
            bigDecimalStyleMap = new HashMap<String, CellStyle>();

            CellStyle defaultStyle = workbook.createCellStyle();
            defaultStyle.setVerticalAlignment(VerticalAlignment.CENTER);
            styleMap.put(null, defaultStyle);

            CellStyle defaultBigDecimalStyle = workbook.createCellStyle();
            defaultBigDecimalStyle.cloneStyleFrom(defaultStyle);
            defaultBigDecimalStyle.setAlignment(HorizontalAlignment.RIGHT);
            defaultBigDecimalStyle.setDataFormat((short) 4);
            bigDecimalStyleMap.put(null, defaultBigDecimalStyle);


            CellStyle linkStyle = workbook.createCellStyle();
            linkStyle.cloneStyleFrom(defaultStyle);
            Font linkFont = workbook.createFont();
            linkFont.setUnderline((byte) 1);
            linkFont.setColor((short) 0xc);
            linkStyle.setFont(linkFont);


            ClientRequest request;
            while (!((request = (ClientRequest) ois.readObject()).getActions()[0] instanceof Build)) {
                Exception ex = null;
                try {
                    ClientMessage[] actions = request.getActions();
                    for (ClientMessage action : actions) {
                        if (action instanceof CreateSheet) {
                            if (sheet != null)
                                sheet.flushRows();
                            CreateSheet createSheet = (CreateSheet) action;
                            if (createSheet.getName() == null)
                                sheet = workbook.createSheet();
                            else
                                sheet = workbook.createSheet(createSheet.getName());
                            rowIndex = 0;
                            sheet.setDefaultRowHeight((short) (sheet.getDefaultRowHeight() * 1.5));
                        } else if (action instanceof CreateFreezeTop) {
                            CreateFreezeTop createFreezeTop = (CreateFreezeTop) action;
                            sheet.createFreezePane(0, createFreezeTop.getRowCount());
                        } else if (action instanceof CreateLine) {
                            CreateLine createLine = (CreateLine) action;
                            createLine(createLine);
                        } else if (action instanceof CreateLines) {
                            CreateLines createLines = (CreateLines) action;
                            for (CreateLine createLine : createLines.getLines()) {
                                createLine(createLine);
                            }
                        } else if (action instanceof CreateStyle) {
                            CreateStyle createStyle = (CreateStyle) action;
                            CellStyle cellStyle = workbook.createCellStyle();
                            cellStyle.cloneStyleFrom(defaultStyle);
                            cellStyle.setFillForegroundColor(createStyle.getBackgroundColor());
                            cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                            Font font = workbook.createFont();
                            font.setColor(createStyle.getFontColor());
                            cellStyle.setFont(font);
                            switch (createStyle.getHorizontal()) {
                                case 1:
                                    cellStyle.setAlignment(HorizontalAlignment.LEFT);
                                    break;
                                case 2:
                                    cellStyle.setAlignment(HorizontalAlignment.CENTER);
                                    break;
                                case 3:
                                    cellStyle.setAlignment(HorizontalAlignment.RIGHT);
                                    break;
                            }
                            switch (createStyle.getVertical()) {
                                case 1:
                                    cellStyle.setVerticalAlignment(VerticalAlignment.TOP);
                                    break;
                                case 2:
                                    cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                                    break;
                                case 3:
                                    cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
                                    break;
                            }
                            String uuid = createStyle.getUuid();
                            styleMap.put(uuid, cellStyle);
                            CellStyle bigDecimalStyle = workbook.createCellStyle();
                            bigDecimalStyle.cloneStyleFrom(cellStyle);
                            bigDecimalStyle.setAlignment(HorizontalAlignment.RIGHT);
                            bigDecimalStyle.setDataFormat((short) 4);
                            bigDecimalStyleMap.put(uuid, bigDecimalStyle);
                        } else if (action instanceof CreateMerge) {
                            CreateMerge createMerge = (CreateMerge) action;
                            sheet.addMergedRegion(new CellRangeAddress(createMerge.getFirstRow(), createMerge.getLastRow(), createMerge.getFirstCol(), createMerge.getLastCol()));
                        } else if (action instanceof CreateMerges) {
                            CreateMerges createMerges = (CreateMerges) action;
                            for (CreateMerge createMerge : createMerges.getCreateMerges())
                                sheet.addMergedRegion(new CellRangeAddress(createMerge.getFirstRow(), createMerge.getLastRow(), createMerge.getFirstCol(), createMerge.getLastCol()));
                        } else if (action instanceof CreateDefaultSimpleDateFormat) {
                            CreateDefaultSimpleDateFormat createDefaultSimpleDateFormat = (CreateDefaultSimpleDateFormat) action;
                            simpleDateFormat = createDefaultSimpleDateFormat.getSimpleDateFormat();
                        } else if (action instanceof CreateLink) {
                            CreateLink createLink = (CreateLink) action;
                            HyperlinkType type = null;
                            switch (createLink.getType()) {
                                case DOCUMENT:
                                    type = HyperlinkType.DOCUMENT;
                                    break;
                                case FILE:
                                    type = HyperlinkType.FILE;
                                    break;
                                case URL:
                                    type = HyperlinkType.URL;
                                    break;
                                case MAIL:
                                    type = HyperlinkType.EMAIL;
                                    break;
                            }
                            Hyperlink link = createHelper.createHyperlink(type);
                            link.setAddress(createLink.getAddress());
                            SXSSFRow row = sheet.getRow(createLink.getRow());
                            SXSSFCell cell = row.getCell(createLink.getCol());
                            cell.setHyperlink(link);
                            cell.setCellStyle(linkStyle);
                        } else if (action instanceof AutoSetWidth) {
                            // 此方法被client禁用,详见client源码
                            AutoSetWidth autoSetWidth = (AutoSetWidth) action;
                            for (int colIndex = autoSetWidth.getStartCol(); colIndex <= autoSetWidth.getStopCol(); colIndex++)
                                sheet.autoSizeColumn(colIndex);
                        } else if (action instanceof SetColumnWidth) {
                            SetColumnWidth setColumnWidth = (SetColumnWidth) action;
                            sheet.setColumnWidth(setColumnWidth.getColumnIndex(), setColumnWidth.getWidth());
                        }
                    }
                } catch (Exception e) {
                    ex = e;
                }
                if (ex == null) {
                    sendSuccessMessage();
                } else {
                    sendErrorMessage(ex);
                    throw ex;
                }
            }
            if (Build.BUILD.equals(request.getActions()[0])) {
                //收到Build请求
                //输出
                sendSuccessMessage();
                workbook.write(oos);
                workbook.close();
            } else {
                sendSuccessMessage();
                workbook.close();
            }
        } catch (Exception e) {
            if (workbook != null) {
                try {
                    workbook.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            Logger.error(tid + " ：对话中途发生错误，退出线程", e);
        } finally {
            closeAll();
        }
        Logger.info(tid + " ：结束处理");
    }

    private void createLine(CreateLine createLine) {
        SXSSFRow row = sheet.createRow(rowIndex++);
        Object[] objects = createLine.getObjects();

        CellStyle normalCellStyle = styleMap.get(createLine.getCellStyle());
        CellStyle bigDecimalCellStyle = bigDecimalStyleMap.get(createLine.getCellStyle());

        for (int i = 0; i < objects.length; i++) {
            SXSSFCell cell = row.createCell(i);
            Object item = objects[i];
            CellStyle cellStyle = normalCellStyle;
            if (item == null) {
                //cell.setCellType(CellType._NONE);
            } else if (item instanceof String) {
                cell.setCellType(CellType.STRING);
                cell.setCellValue((String) item);
            } else if (item instanceof Number) {
                cell.setCellType(CellType.NUMERIC);
                if (item instanceof BigDecimal) {
                    cell.setCellValue(((BigDecimal) item).doubleValue());
                    cellStyle = bigDecimalCellStyle;
                } else {
                    cell.setCellValue(((Number) item).toString());
                }
            } else if (item instanceof Date) {
                cell.setCellType(CellType.STRING);
                cell.setCellValue(simpleDateFormat.format((Date) item));
            } else {
                cell.setCellType(CellType.STRING);
                cell.setCellValue(item.toString());
            }
            if (cellStyle != null) {
                cell.setCellStyle(cellStyle);
            }
        }
    }


    //如果过程发生异常则关闭链接
    private void closeAll() {
        try {
            if (ois != null)
                ois.close();
        } catch (Throwable e) {
        }
        try {
            if (oos != null)
                oos.close();
        } catch (Throwable e) {
        }
        try {
            if (socket != null)
                socket.close();
        } catch (Throwable e) {
        }
    }

    //发出一个指令
    private void sendMessage(ServerStatus serverStatus, Exception e) throws IOException {
        ServerResponse serverResponse = new ServerResponse();
        serverResponse.setStatus(serverStatus);
        serverResponse.setException(e);
        oos.writeObject(serverResponse);
        oos.flush();
    }

    private void sendSuccessMessage() throws Exception {
        sendMessage(ServerStatus.SUCCESS, null);
    }

    private void sendErrorMessage(Exception ex) throws IOException {
        sendMessage(ServerStatus.EXCEPTION, ex);
    }

    private void sendReadyMessage() throws IOException {
        sendMessage(ServerStatus.READY, null);
    }


}
